/**
*  \file candispatcher.c
*
*  \brief
*
*  \author Michael J. Hohmann  <michael.hohmann@linde-mh.de>
*
*
*/

/*===============================================[ private includes  ]================================================*/
#include "candispatcher.h"
#include "../assertor/assertor.h"
#include "../pal/pal_mem.h"
#include "../mem/mem.h"
#include "../bfind/bfind.h"

/*===============================================[ private defines   ]================================================*/

/*===============================================[ private datatypes ]================================================*/

/*===============================================[ private variables ]================================================*/

/*===============================================[ private functions ]================================================*/
static enum compare compare( void const * a, void const * b);

/*================================================[ inline functions ]================================================*/
STATIC_INLINE enum compare candispatcher_find_id( struct candispatcher_obj *me, canmsg_id_t id ,  struct candispatcher_entry **p );
STATIC_INLINE void candispatcher_set_entry( struct candispatcher_entry *p, canmsg_id_t id, candispatcher_callback_function_t callback, void *callback_dat );

/*================================================[ public functions ]================================================*/

RC candispatcher_construct( struct candispatcher_obj* me ,struct candispatcher_entry *table, pal_size_t table_size )
{
   ASSERTOR_PRE( NIL != me,"NIL is not allowed as object pointer." );

   pal_memzero( me, sizeof *me);

   me->enable=FALSE;
   me->table=table;
   me->table_size=table_size;

   return RC_SUCCESS;
}

void candispatcher_install_default_callback( struct candispatcher_obj* me, candispatcher_callback_function_t callback )
{
   BO enable;
   ASSERTOR_PRE( NIL != me,"NIL is not allowed as object pointer." );

   enable=me->enable;
   me->enable=FALSE;
   me->default_callback=callback;
   me->enable=enable;
}

void candispatcher_install_callback( struct candispatcher_obj* me, canmsg_id_t id, candispatcher_callback_function_t callback, void* callback_data )
{
	ASSERTOR_PRE( NIL != me,"NIL is not allowed as object pointer." );
   {
		struct candispatcher_entry *p= NIL ;

		switch( candispatcher_find_id( me, id , &p ) )
		{
		   case compare_a_less_b:
		   {
            if(me->table_size > me->current_size)
            {
               mem_move( p+1, p , (pal_size_t)(U8*)((U8*)&me->table[me->current_size]-(U8*)p) ); /* make space */
               candispatcher_set_entry( p, id,callback, callback_data);
               me->current_size++;
            }
		      break;
		   }
         case compare_a_equals_b:
         {
            /* id is already registered  */
            if( NIL == callback) /* remove entry  */
            {
              /* id is already registered */
               mem_move( p, p+1 , &me->table[me->current_size]-p-1 ); /* make space */
               candispatcher_set_entry( p, id, callback, callback_data);
               me->current_size--;
            }
            else
            {  /* overwrite existing entry */
               candispatcher_set_entry( p, id,callback, callback_data);
            }
            break;
         }
         case compare_a_greater_b:
         {  /* add new element at the end of the list */
            if(me->table_size > me->current_size)
            {
               candispatcher_set_entry( p, id, callback, callback_data);
               me->current_size++;
            }
            break;
         }
         default:
         {
            ASSERTOR_ILLEGAL_DEFAULT("unexpected result of comparison.");
         }
		}
   }
}

RC candispatcher_enable(struct candispatcher_obj* me, BO value )
{
   ASSERTOR_PRE( NIL != me,"NIL is not allowed as object pointer." );
   me->enable=value;
   return RC_SUCCESS;
}



void candispatcher_dispatch_message(struct candispatcher_obj *me, struct canmsg *msg)
{
   ASSERTOR_PRE( NIL != me,"NIL is not allowed as object pointer." );
   ASSERTOR_PRE( NIL != msg,"NIL is not allowed as message pointer." );

   if( me->enable)
   {
		struct candispatcher_entry *p= NIL ;
	   if( compare_a_equals_b == candispatcher_find_id( me, canmsg_get_id(msg), &p ))
	   {
		   p->callback( msg, p->callback_data );
	   }
	   else
	   {
         if( NIL != me->default_callback) /* if a default callback was installed */
         {
            me->default_callback( msg, p->callback_data );
         }
      }
   }
}


/*===============================================[ private functions ]================================================*/
STATIC_INLINE enum compare candispatcher_find_id( struct candispatcher_obj *me, canmsg_id_t id ,  struct candispatcher_entry **p )
{
   return bfind( &id, me->table, me->current_size, sizeof( me->table[0] ), compare, (void*) p );
}

static enum compare compare( void const * a, void const * b)
{
   canmsg_id_t a_value = *((canmsg_id_t*) a);
   canmsg_id_t b_value = ((struct candispatcher_entry * ) b )->id;

   if( a_value > b_value )
   {
      return compare_a_greater_b;
   }

   if( a_value < b_value)
   {
      return compare_a_less_b;
   }

   return compare_a_equals_b;
}

STATIC_INLINE void candispatcher_set_entry( struct candispatcher_entry *item, canmsg_id_t id, candispatcher_callback_function_t callback, void *callback_dat )
{
   item->id=id;
   item->callback=callback;
   item->callback_data=callback_dat;
}



/***********************************************************************************************************************
*                             Copyright 2018 Linde Material Handling. All rights reserved.                             *
***********************************************************************************************************************/
